<?php

namespace app\user\logic;

use app\Api;

/**
 * 用户微信类
 * Class UserWeChat
 * @package app\index\logic
 */
class UserWeChat extends Api
{
    private static $obj = null;

    private $app_id; //微信appId

    private $mch_id; //商户号

    private $pay_key; //支付密钥

    private $ca_pem_path; //ca证书路径

    private $cert_pem_path; //cert证书路径

    private $key_pem_path; //key证书路径

    public function _initialize()
    {
        parent::_initialize();

        $this->app_id = config('wechatpay.appid');
        $this->mch_id = config('wechatpay.mch_id');
        $this->pay_key = config('wechatpay.pay_key');
        $this->ca_pem_path = config('wechatpay.ca_pem_path');
        $this->cert_pem_path = config('wechatpay.cert_pem_path');
        $this->key_pem_path = config('wechatpay.key_pem_path');
    }


    /**
     * 类入口
     * @return UserWeChat|null
     */
    public static function getInstance()
    {
        if (is_null(self::$obj))
            self::$obj = new self();
        return self::$obj;
    }

    /**
     * 微信APP支付
     * @param int $user_id 用户ID
     * @param int $order_id 订单ID
     * @param string $order_sn 订单编号
     * @param float $order_amount 支付金额（分为单位）
     * @param string $body 支付描述
     * @param string $notify_url 回调地址
     */
    public function weChatAppPay($pay_sn, $order_amount, $body, $notify_url, $attach = [])
    {
        $data['appid'] = $this->app_id; //微信开放平台审核通过的应用APPID
        $data['mch_id'] = $this->mch_id; //微信支付分配的商户号
        $data['device_info'] = 'WEB'; //终端设备号(门店号或收银设备ID)，默认请传"WEB"
        $data['nonce_str'] = $this->_getRandomString(32); //随机字符串，不长于32位
        $data['body'] = $body;//商品描述交易字段格式根据不同的应用场景按照以下格式：APP——需传入应用市场上的APP名字-实际商品名称，天天爱消除-游戏充值。
        $data['out_trade_no'] = $pay_sn; //商户系统内部的订单号,32个字符内、可包含字母, 其他说明见商户订单号
        $data['total_fee'] = $order_amount * 100; //订单总金额，单位为分
        $data['spbill_create_ip'] = $this->getIP(); //用户端实际ip
        $data['notify_url'] = $notify_url; //接收微信支付异步通知回调地址，通知url必须为直接可访问的url，不能携带参数。
        $data['trade_type'] = 'APP'; //支付类型
        $data['attach'] = urlencode(json_encode($attach)); //附加数据

        $res_sign = $this->weChatSign($data);
        $res = $this->xmlToArray($this->_curlPost('https://api.mch.weixin.qq.com/pay/unifiedorder', $res_sign['xml']));
        if ($res['return_code'] == 'SUCCESS' && $res['result_code'] == 'SUCCESS') {
            $orderInfo['appid'] = $res['appid']; //应用APPID
            $orderInfo['partnerid'] = $res['mch_id']; //商户号
            $orderInfo['noncestr'] = $res['nonce_str']; //随机字符串
            $orderInfo['prepayid'] = $res['prepay_id']; //预支付交易会话标识
            $orderInfo['package'] = 'Sign=WXPay'; //交易时间
            $orderInfo['timestamp'] = $this->_now; //交易时间
            $res_sign2 = $this->weChatSign($orderInfo);
            $orderInfo['sign'] = $res_sign2['sign']; //签名
            return $orderInfo;
        } else {
            return false;
        }
    }

    /**
     * 微信支付退款
     * @param string $out_trade_no 订单号
     * @param float $refund_fee 退款金额
     * @param string $out_refund_no 唯一标识
     * @param float $total_fee 总金额
     * @param int $op_user_id 操作员id
     * @return \think\response\Json
     *, $op_user_id = 1
     */
    public function weChatRefund($out_trade_no, $refund_fee, $out_refund_no, $total_fee)
    {
        $data['appid'] = $this->app_id; //微信开放平台审核通过的应用APPID
        $data['mch_id'] = $this->mch_id; //微信支付分配的商户号
        //$data['device_info'] = 'WEB'; //终端设备号(门店号或收银设备ID)，默认请传"WEB"
        $data['nonce_str'] = $this->_getRandomString(32); //随机字符串，不长于32位
        $data['out_trade_no'] = $out_trade_no;//商户端订单号
        $data['out_refund_no'] = $out_refund_no;//唯一标识
        $data['total_fee'] = $total_fee * 100;//订单金额
        $data['refund_fee'] = $refund_fee * 100;//退款金额
        $data['op_user_id'] = '1419727002';//操作员id
        $res_sign = $this->weChatSign($data);
        $res = $this->xmlToArray($this->curlPostSsl('https://api.mch.weixin.qq.com/secapi/pay/refund', $res_sign['xml']));
        return json($res); //json
    }

    /*************************微信支付签名需要使用到的方法*******************************/
    /**
     * 微信退款 ssl Curl Post数据
     * @param string $url 接收数据的api
     * @param string $vars 提交的数据
     * @param int $second 要求程序必须在$second秒内完成,负责到$second秒后放到后台执行
     * @param array $aHeader 请求头内容
     * @return bool|mixed 成功且对方有返回值则返回
     */
    private function curlPostSsl($url, $vars, $second = 30, $aHeader = array('Expect:'))
    {
        $ch = curl_init();
        //curl_setopt($ch,CURLOPT_VERBOSE,'1');
        curl_setopt($ch, CURLOPT_TIMEOUT, $second);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_URL, $url);
        //curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        //curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);

        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);   // 只信任CA颁布的证书
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); // 检查证书中是否设置域名，并且是否与提供的主机名匹配
        curl_setopt($ch, CURLOPT_CAINFO, $this->ca_pem_path); // CA根证书（用来验证的网站证书是否是CA颁布）
        curl_setopt($ch, CURLOPT_SSLCERTTYPE, 'PEM');
        curl_setopt($ch, CURLOPT_SSLCERT, $this->cert_pem_path);
        curl_setopt($ch, CURLOPT_SSLCERTPASSWD, '1419727002');
        curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM');
        curl_setopt($ch, CURLOPT_SSLKEY, $this->key_pem_path);

        if (count($aHeader) >= 1) {
            curl_setopt($ch, CURLOPT_HTTPHEADER, $aHeader);
        }

        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $vars);
        $data = curl_exec($ch);
        curl_close($ch);
        if ($data) {
            return $data;
        } else {
            return false;
        }
    }

    /**
     * @param array $data
     * @return array
     */
    public function weChatSign($data)
    {
        ksort($data);

        $sign = '';
        $xml = '<xml>';
        foreach ($data as $key => $value) {
            if (empty($value) === false) {
                $sign .= $key . '=' . $value . '&';
                $xml .= '<' . $key . '><![CDATA[' . $value . ']]></' . $key . '>';
            }
        }
        unset($key, $value);

        $sign .= 'key=' . $this->pay_key;

        $sign = strtoupper(md5($sign));

        $xml .= '<' . 'sign><![CDATA[' . $sign . ']]></sign></xml>';

        return ['sign' => $sign, 'xml' => $xml];
    }
}